/*->c.serial */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>

#include "h.os"
#include "h.wimp"
#include "h.bbc"
#include "h.flex"
#include "h.swis"
#include "h.kernel"

#include "h.wos"
#include "h.main"
#include "h.ram"
#include "h.mym"

#include "h.key"

#include "h.xext"
#include "h.pr"
#include "h.script"

#include "h.mnpx"
#include "h.vax"

#include "h.serialdev"

#include "h.serial"


/***************************************************************************/
/*

 Code to handle serial port

*/
/***************************************************************************/

int traps;                    /* number of serial traps     */


typedef struct trapstate
{
 int  traphit;                 /* trap number of hit         */
 int  traplo;                  /* trap buffer lo pointer     */
 int  trapslo;                 /* send lo byte               */
 int  trapshi;                 /* send hi byte               */
 int  traphi;                  /* trap buffer hi pointer     */
 char trapbuff[128];           /* trap buffer                */
 int  n;                       /* number of traps            */
} trapstate;


trapstate rxtrap;
trapstate txtrap;


#define TRAPMASK 0x7F



typedef struct trapstr
{
 char  fn[32];                /* name of function to call  */
 char  match[96];             /* string to match           */
 int   len;                   /* length of string to match */
 int   type;                  /* 0 intercept 1 pass on     */
 int   dir;
} trapstr;

trapstr * trap;



/*

void settrap(int txrx,int type,int add,string & function,string & trap);

*/


void settrap(int fp)
{
 int  i;
 int  j;
 char fn[32];
 char match[96];
 int  len;
 int  add;
 int  dir;
 int  type;


 dir= stack[fp];
 type=stack[fp+1];
 add= stack[fp+2];

 strcpy(fn,stringptr(stack[fp+3]));
 convert(stringptr(stack[fp+4]),match,&len,96);

 for(i=0;i<traps;i++)
 {
  if(!strcmp(fn,trap[i].fn) && trap[i].dir==dir)
  {
   for(j=0;j<len;j++)
    if(trap[i].match[j]!=match[j]) break;
   if(j==len) break;
  }
 }

 if(i<traps && !add)   /* we hit a trap       */
 {
  flex_midextend((flex_ptr)&trap,sizeof(trapstr)*(i+1),-sizeof(trapstr));

  traps--;
  if(dir) rxtrap.n--;
  else    txtrap.n--;
 }
 else                  /* could not find trap */
 if(i==traps && add)
 {
  if(flex_extend((flex_ptr)&trap,sizeof(trapstr)*(traps+1)))
  {
   memcpy(trap[traps].fn,fn,32);
   memcpy(trap[traps].match,match,96);
   trap[traps].len=len;
   trap[traps].type=type;
   trap[traps].dir=dir;

   traps++;
   if(dir) rxtrap.n++;
   else    txtrap.n++;
  }
  else zraise(ZMEM,"trap");

 }
}


/* return 1 if a match, or else 0 */
/* object has to be to get trapshi as far towards traphi as possible */


int trapcalcstate(trapstate * s,int dir)
{
 int     i;
 int     j;
 char *  match;
 int     len;
 int     ptr;
 int     newtrapshi=s->traphi;
 int     amatch=0;

 if(s->traplo!=s->traphi)
 {
  for(i=0;i<traps;i++)
  {
   if(trap[i].dir==dir)
   {
    match=trap[i].match;
    len=trap[i].len;
    ptr=s->traplo;
    if(len)
    {
     j=0;
     while(1)
     {
      if(match[j]!=s->trapbuff[ptr & TRAPMASK])
      {
       j=0;      /* this means that no way can this trap ever match */
       break;
      }
      j++;
      if(j==len)
      {
       s->traphit=i;
       return(1);
      }

      ptr++;
      if(ptr==s->traphi)
      {
       amatch=1;
       break;
      }
     }

     if(trap[i].type==1)  /* intercept */
     {
      if(j==0 && newtrapshi>s->traplo) newtrapshi=s->traplo+1;
      else                             newtrapshi=s->traplo;
     }
    }
   }
  }


  if(amatch) s->trapshi=newtrapshi;
  else
  {
   s->traplo++;                                     /* no match */
/*   if(s->trapshi<s->traplo) s->trapshi=s->traplo;  */
   s->trapshi=newtrapshi;
  }
 }
 return(0);
}


void trapaddbyte(int byte,trapstate * s)
{
 if(byte==-1) return;
 s->trapbuff[(s->traphi)++ & TRAPMASK]=byte;
}


int traprembyte(trapstate * s)
{
 if(s->trapslo==s->trapshi) return(-1);
 else                       return(s->trapbuff[(s->trapslo)++ & TRAPMASK]);
}


/* used on RX */

int traprxbyte(void)
{
 int  i;
 char string[128];
 int  r0;

 if(rxtrap.trapslo==rxtrap.trapshi)
 {
  trapaddbyte(inlinktype?linkgetbyte():getbytelo(),&rxtrap); 

  if(trapcalcstate(&rxtrap,1))
  {
   memcpy(string,trap[rxtrap.traphit].match,96);
   string[trap[rxtrap.traphit].len]=0;

   if(xexec3(trap[rxtrap.traphit].fn,string,&r0))
   {
    if(r0)
    {
     i=0;
     rxtrap.trapslo=rxtrap.traphi;
     while(string[i]) rxtrap.trapbuff[rxtrap.traphi++ & TRAPMASK]=string[i++];
    }
   }
   rxtrap.traplo=rxtrap.trapshi=rxtrap.traphi;
  }
 }

 return(traprembyte(&rxtrap));
}




/* used on TX */

void traptxbyte(int byte)
{
 int  i;
 char string[128];
 int  r0;

 trapaddbyte(byte,&txtrap); 

 if(trapcalcstate(&txtrap,0))
 {
  memcpy(string,trap[txtrap.traphit].match,96);
  string[trap[txtrap.traphit].len]=0;

  if(xexec3(trap[txtrap.traphit].fn,string,&r0))
  {
   if(r0)
   {
    i=0;
    txtrap.trapslo=txtrap.traphi;
    while(string[i]) txtrap.trapbuff[txtrap.traphi++ & TRAPMASK]=string[i++];
   }
  }
  txtrap.traplo=txtrap.trapshi=txtrap.traphi;
 }


/* dprintf(0,"trapslo=%d traplo=%d trapshi=%d traphi=%d",
          txtrap.trapslo,txtrap.traplo,txtrap.trapshi,txtrap.traphi); */

 while(txtrap.trapslo<txtrap.trapshi)
 {
  while((byte=traprembyte(&txtrap))!=-1) 
  {
   if(inlinktype) linkoutbyte(byte);
   else           outbytelo(byte);
  }
  if(txtrap.traplo<txtrap.traphi) trapcalcstate(&txtrap,0);
  else break;
 }
}



/***************************************************************************/

int getswi=OS_SerialOp;
int getr0=4;
int getchannel=0;
int getport;


int putswi=OS_SerialOp;
int putr0=3;
int putchannel=0;
int putport;


#define BLOCKSIZE 0x100
#define BLOCKMASK 0xFF


char putbuff[BLOCKSIZE];
char getbuff[BLOCKSIZE];
int  putwrite;
int  putread;
int  getwrite;
int  getread;








int ss_swix(int swicode,int r0,int r1,int r2)
{
 _kernel_swi_regs rx;
 int              carry;

 rx.r[0]=r0;
 rx.r[1]=r1;
 rx.r[2]=r2;

 _kernel_swi_c(swicode,&rx,&rx,&carry);

 return(carry?-1:rx.r[1]);
}


int ss_swix2(int swicode,int r0,int r1,int r2)
{
 _kernel_swi_regs rx;

 rx.r[0]=r0;
 rx.r[1]=r1;
 rx.r[2]=r2;

 _kernel_swi(swicode,&rx,&rx);

 return(rx.r[0]);
}









static void blockinit(void)
{
 putwrite=putread=0;
 getwrite=getread=0;
}



static int blockread(int port,char * data,int n)
{
 return(ss_swix2(getswi,getr0,(int)data,n));
}


static int blockwrite(int port,char * data,int n)
{
 return(ss_swix2(putswi,putr0,(int)data,n));
}



/* attempt to write n bytes of data, return number of bytes written */

static int putwriteblock(char * data,int n)
{
 if(putchannel==3) return(blockwrite(putr0,data,n));
 else
 if(putchannel==4) return(sdevblockwrite(putport,data,n));
 else              return(n);
}



/* attempt to flush put buffer, return bytes written */

static int putflush(void)
{
 int wr;

 wr=0;

 if(putread!=putwrite)
 {
  if(putread>putwrite)
  {
   wr=putwriteblock(putbuff+putread,BLOCKSIZE-putread);
   if(!wr) return(wr);
   putread=(putread+wr) & BLOCKMASK;
  }

  if(putwrite>putread)
  {
   wr=putwriteblock(putbuff+putread,putwrite-putread);
   putread=(putread+wr) & BLOCKMASK;
  }
 }

 return(wr);
}


void blockzero(void)
{
 putflush();
}


/* return -1 if failed */

int blockput(int byte)
{
 int next;

 next=(putwrite+1) & BLOCKMASK;

 if(next==putread && !putflush()) return(-1);

 putbuff[putwrite]=byte;
 putwrite=next;

 if(!scriptpoll)
 {
  while(putread!=putwrite) putflush();
 }

 return(byte);
}


/* return -1 or byte */

int blockget(void)
{
 int byte;

 if(getwrite==getread)
 {
  getread=getwrite=0;
  if(getchannel==3) getwrite=blockread(getr0,getbuff,BLOCKMASK);
  else
  if(getchannel==4) getwrite=sdevblockread(getport,getbuff,BLOCKMASK);
 }

 if(getwrite==getread) return(-1);
 else
 {
  byte=getbuff[getread];
  getread=(getread+1) & BLOCKMASK;
  return(byte);
 }
}




/* return -1 or byte */

int getbytelo(void)
{
 if(!getchannel) return(ss_swix(getswi,getr0,0,0));
 else
 if(getchannel==2) return(serialdevget(getport));
 else
 if(getchannel==3 || getchannel==4) return(blockget());
 else
 if(getchannel==1)
 {
  int byte;
  if(xexec("getbyte",NULL,NULL,&byte)) return(byte);
  else                                 return(-1);
 }
 else return(-1);
}




int linkgetbyte(void)
{
 if(vasscom) return(vaxgetbyte());
 else        return(mnpgetbyte());
}




int getbyte(void)
{
 if(rxtrap.n)
 {
  return(traprxbyte());
 }
 else
 {
  if(inlinktype) return(linkgetbyte());
  else           return(getbytelo()); 
 }
}



/*

int getbyte(void)
{
 static FILE * fp;
 int         byte;

 if(!fp) fp=fopen("$.true","wb");

 byte=zzgetbyte();

 if(byte!=-1) putc(byte,fp);

 return(byte);
}

*/


void outbytelo(int byte)
{
 if(!putchannel)
 {
  while(ss_swix(putswi,putr0,byte,0)==-1)
  {
   if(scriptpoll) pollzt();
  }
 }
 else
 if(putchannel==2)
 {
  while(serialdevput(byte,putport)==-1)
  {
   if(scriptpoll) pollzt();
  }
 }
 else
 if(putchannel==3 || putchannel==4)
 {
  while(blockput(byte)==-1)
  {
   if(scriptpoll) pollzt();
  }
 }
 else
 if(putchannel==1)
 {
  while(1)
  {
   if(xexec("putbyte",NULL,&byte,&byte))
   {
    if(byte!=-1) break;
   }
   else break;

   if(scriptpoll) pollzt();
  }
 }
}



void linkoutbyte(int byte)
{
 if(vasscom) vaxoutbyte(byte);
 else        mnpoutbyte(byte);
}



int outbyte(int byte)
{     
 if(txtrap.n)
 {
  traptxbyte(byte);
 }
 else
 {
  if(inlinktype) linkoutbyte(byte);
  else           outbytelo(byte);
 }

 return(0);
}



void setchannel(int fp)
{
 if(stack[fp]==0)
 {
  if(stack[fp+1]==1)
  {
   putchannel=1;
  }
  else
  if(stack[fp+1]==2 || stack[fp+1]==4)
  {
   putchannel=stack[fp+1];
   putport=stack[fp+3];
  }
  else
  {
   putswi=stack[fp+2];
   putr0=stack[fp+3];
   putchannel=stack[fp+1];
  }
 }
 else
 if(stack[fp]==1)
 {
  if(stack[fp+1]==1)
  {
   getchannel=1;
  }
  else
  if(stack[fp+1]==2 || stack[fp+1]==4)
  {
   getchannel=stack[fp+1];
   getport=stack[fp+3];
  }
  else
  {
   getswi=stack[fp+2];
   getr0=stack[fp+3];
   getchannel=stack[fp+1];
  }
 }


 if(putchannel==3 || putchannel==4) addzeroevent(BLOCKZERO);
 else                               remzeroevent(BLOCKZERO);
 blockinit();
}


/* set up serial system */

void serialset(void) 
{
 flex_alloc((flex_ptr)&trap,0);
}


/*****************************************************************************/


/* convert string and send it to line, return 0 if OK, else 1 */

int convertstringline(char * string)
{
 return(expandmacro(outbyte,string));
}

